﻿using NetworkLib;
using NetworkLib.Utilities;
using PBO.Network.Domain;
using PBO.Network.Enumerations;
using PBO.Network.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PBO.Network.Proxy
{
    public class BattleAgentServer : BattleServer
    {
        private int _battleIdentityBase = -1;
        private Dictionary<int, BattleAgent> _agents = new Dictionary<int, BattleAgent>();

        public BattleAgentServer()
            : base((int)ServerPort.BattleAgentServerPort)
        {
            this.UpdateInterval = 500;
            this.OnLogicUpdate += BattleAgentServer_OnLogicUpdate;
            this.OnClientDisconnected += BattleAgentServer_OnClientDisconnected;
        }

        void BattleAgentServer_OnClientDisconnected(ClientSession client)
        {
            var session = client as BattleAgentSession;

            if (session.AgentID != -1)
            {
                BattleAgent agent = GetAgent(session.AgentID);
                if (agent != null && !agent.BattleEnd)
                {
                    agent.PlayerExit(client.SessionID);
                }
            }
        }

        void BattleAgentServer_OnLogicUpdate()
        {
            var keys = _agents.Where(t => t.Value.BattleEnd).Select(t => t.Key).ToArray();
            foreach (int key in keys)
            {
                _agents.Remove(key);
                Logger.LogInfo("Remove battle agent, ID : {0}", key);
            }
        }

        protected override ClientSession CreateClientSession(int sessionID, IReactor reactor)
        {
            return new BattleAgentSession(sessionID, reactor, Buffered);
        }

        public int AddBattle(BattleMode mode, BattleRule[] rules)
        {
            _battleIdentityBase++;
            var agent = new BattleAgent(mode, rules, this);
            _agents[_battleIdentityBase] = agent;

            Logger.LogInfo("Add battle agent, ID : {0}", _battleIdentityBase);
            return _battleIdentityBase;
        }

        #region send

        public void SendTeam(int sessionID, byte position, string identity, byte[] byteSequence)
        {
            var rsp = new BattleTeam();
            rsp.position = position;
            rsp.identity = identity;
            rsp.team = byteSequence;
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVETEAM_HASH, rsp);
        }

        public void SendMove(int sessionID, PlayerMove move)
        {
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVEMOVE_HASH, move);
        }

        public void SendExitMessage(int sessionID, string identity)
        {
            base.Send(sessionID, BattleMethods.CLIENT_EXIT_HASH, identity);
        }

        public void SendTieMessage(int sessionID, string identity, TieMessage message)
        {
            var req = new TieMessageRequest();
            req.identity = identity;
            req.message = message;
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVETIEMESSAGE_HASH, req);
        }

        public void SendBattleData(int sessionID, BattleRule[] rules, int randomSeed)
        {
            var req = new BattleRuleSequence();
            req.Rules = rules;
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVERULES_HASH, req);
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVERANDOMSEED_HASH, randomSeed);
        }

        public void SendBattleSnapshot(int sessionID, BattleSnapshot snapshot)
        {
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVEBATTLESNAPSHOT_HASH, snapshot);
        }

        public void SendBattleInfo(int sessionID, BattleInfo info)
        {
            base.Send(sessionID, BattleMethods.CLIENT_RECEIVEBATTLEINFO_HASH, info);
        }

        public void RegistObserver(int sessionID)
        {
            base.Send(sessionID, BattleMethods.CLIENT_REGISTOBSEVER_HASH, 0);
        }

        public void PlayerTimeUp(int sessionID, string identity)
        {
            base.Send(sessionID, BattleMethods.CLIENT_TIMEUP_HASH, identity);
        }

        #endregion

        private BattleAgent GetAgent(int agentID)
        {
            BattleAgent agent;
            bool contains = _agents.TryGetValue(agentID, out agent);
            if (contains) return agent;
            return null;
        }

        private BattleAgent GetClientAgent(int sessionID)
        {
            ClientSession client = GetClient(sessionID);
            if (client == null || (client as BattleAgentSession).AgentID == -1) return null;
            return GetAgent((client as BattleAgentSession).AgentID);
        }

        #region IPokemonBattleServerService 成员

        public void OnLogon(int sessionID, string identity, BattleMode modeInfo, string versionInfo)
        {
            int agentId;
            BattleAgent agent = null;
            if (int.TryParse(identity, out agentId))
            {
                agent = GetAgent(agentId);
            }
            if (agent != null)
            {
                if (!agent.BattleEnd)
                {
                    (GetClient(sessionID) as BattleAgentSession).AgentID = agentId;
                    agent.UserLogon(sessionID, identity);
                    Send(sessionID, BattleMethods.CLIENT_LOGONSUCCESS_HASH, null);
                }
                else
                {
                    Send(sessionID, BattleMethods.CLIENT_LOGONFAIL_HASH, "对手已退出");
                    Disconnect(sessionID);
                }
            }
            else
            {
                Send(sessionID, BattleMethods.CLIENT_LOGONFAIL_HASH, "无法找到对应的对战代理");
                Disconnect(sessionID);
            }
        }

        public void OnReceiveMove(int sessionID, PlayerMove move)
        {
            BattleAgent agent = GetClientAgent(sessionID);
            if (agent != null)
            {
                agent.ReceiveMove(sessionID, move);
            }
        }

        public void OnReceiveTeam(int sessionID, byte position, string identity, byte[] team)
        {
            BattleAgent agent = GetClientAgent(sessionID);
            if (agent != null)
            {
                agent.ReceiveTeam(sessionID, position, identity, team);
            }
        }

        public void OnExit(int sessionID, string identity)
        {
            Disconnect(sessionID);
        }

        public void OnReceiveTieMessage(int sessionID, string identity, TieMessage message)
        {
            BattleAgent agent = GetClientAgent(sessionID);
            if (agent != null)
            {
                agent.ReceiveTieMessage(sessionID, message);
            }
        }

        public void OnRegistObsever(int sessionID, int identity)
        {
            BattleAgent agent = GetAgent(identity);
            if (agent != null && !agent.BattleEnd)
            {
                agent.RegistObserver(sessionID);
            }
        }

        public void OnReceiveBattleInfo(int sessionID, BattleInfo info)
        {
            BattleAgent agent = GetClientAgent(sessionID);
            if (agent != null)
            {
                agent.ReceiveBattleInfo(info);
            }
        }

        public void OnReceiveBattleSnapshot(int sessionID, BattleSnapshot snapshot)
        {
            BattleAgent agent = GetClientAgent(sessionID);
            if (agent != null)
            {
                agent.ReceiveBattleSnapshot(snapshot);
            }
        }

        public void OnTimeUp(int sessionID, string identity)
        {
            BattleAgent agent = GetClientAgent(sessionID);
            if (agent != null)
            {
                agent.PlayerTimeUp(sessionID, identity);
            }
        }
        #endregion

        protected override void OnReceiveData(int sessionID, int method, ByteArray data)
        {
            base.OnReceiveData(sessionID, method, data);
            switch (method)
            {
                case BattleMethods.SERVER_LOGON_HASH:
                    var logon = DataByteArray.FromByteArray<LogonResponse>(data);
                    OnLogon(sessionID, logon.identity, logon.modeInfo, logon.versionInfo);
                    break;
                case BattleMethods.SERVER_RECEIVEMOVE_HASH:
                    OnReceiveMove(sessionID, DataByteArray.FromByteArray<PlayerMove>(data));
                    break;
                case BattleMethods.SERVER_RECEIVETEAM_HASH:
                    var team = DataByteArray.FromByteArray<BattleTeam>(data);
                    OnReceiveTeam(sessionID, team.position, team.identity, team.team);
                    break;
                case BattleMethods.SERVER_EXIT_HASH:
                    OnExit(sessionID, data.ReadUTF());
                    break;
                case BattleMethods.SERVER_TIMEUP_HASH:
                    OnTimeUp(sessionID, data.ReadUTF());
                    break;
                case BattleMethods.SERVER_RECEIVETIEMESSAGE_HASH:
                    var tie = DataByteArray.FromByteArray<TieMessageRequest>(data);
                    OnReceiveTieMessage(sessionID, tie.identity, tie.message);
                    break;
                case BattleMethods.SERVER_REGISTOBSEVER_HASH:
                    OnRegistObsever(sessionID, data.ReadInt());
                    break;
                case BattleMethods.SERVER_RECEIVEBATTLEINFO_HASH:
                    OnReceiveBattleInfo(sessionID, DataByteArray.FromByteArray<BattleInfo>(data));
                    break;
                case BattleMethods.SERVER_RECEIVEBATTLESNAPSHOT_HASH:
                    OnReceiveBattleSnapshot(sessionID, DataByteArray.FromByteArray<BattleSnapshot>(data));
                    break;
            }
        }
    }
}
